第2回UTXO勉強会 Bitcoinアドレスまとめ
Bitcoinのアドレスとは?
Bitcoinアドレスとは、Bitcoinネットワーク上で送金先を特定するための識別子
Bitcoinを持っている=UTXOのスクリプトを解くことができる
スクリプトのままでは見にくいためフォーマットとしてアドレスがある
Bitcoinアドレスの役割
送金先を特定するための文字列
取引相手に公開して利用する(秘密鍵は絶対に公開しない)
アドレスと公開鍵の関係
公開鍵は秘密鍵から数学的に生成されるが、逆算はほぼ不可能
公開鍵をそのまま使うと長くなるため、ハッシュ関数を使用して短縮化
ハッシュ化によりプライバシー向上(公開鍵そのものは隠される)
公開鍵→アドレスの生成の概要
1. 公開鍵をSHA-256でハッシュ化
2. ハッシュ結果をRIPEMD-160でさらにハッシュ化(HASH160)
3. ネットワークを示すプレフィックスやバージョン情報を追加
4. チェックサム(誤り検知用データ)を追加
5. エンコード(Base58CheckまたはBech32)を実行して最終的なアドレスを生成
代表的なアドレスフォーマットの概要
Bitcoinには主に以下の4つの代表的なアドレスフォーマットがあります。
P2PKH (Pay to Public Key Hash)
最も一般的なアドレス形式
Base58Checkエンコードを使用し、アドレスは通常「1」で始まる
P2SH (Pay to Script Hash)
複雑なスクリプト(マルチシグなど)をアドレスとして表現する形式
Base58Checkエンコードを使用し、アドレスは通常「3」で始まる
P2WPKH (Pay to Witness Public Key Hash)
Segwit対応のアドレス形式(Witnessを用いたアドレス)
Bech32エンコードを使用し、アドレスは「bc1」で始まる
P2WSH (Pay to Witness Script Hash)
Segwit対応で複雑なスクリプトを格納できる形式
Bech32エンコードを使用し、アドレスは「bc1」で始まる
各アドレスフォーマット詳細解説
P2PKH
最も基本的なBitcoinアドレスフォーマット
ベース58チェックエンコーディング (1で始まる)
生成の流れ:
公開鍵 → SHA-256 → RIPEMD-160 → version byte追加 → チェックサム追加 → Base58Checkエンコード
例:
公開鍵(compressed): 02b463ff6b2a9b9e0a7a1ad3b1d1c3f4e5d6c7b8a9b0c1d2e3f4a5b6c7d8e9f0a1
HASH160(公開鍵をSHA-256→RIPEMD-160): e8c300c87986efa84c37c0519929019ef86eb5b4
Version byte追加 (mainnetなら0x00): 00e8c300c87986efa84c37c0519929019ef86eb5b4
Checksum追加→Base58Check: 1N5Z2g3Ph4XbPJiFn6jDsVp8eBL6M6M6nT
検証スクリプトの流れ(ScriptPubKey, ScriptSigの解説)
送金時のScriptPubKey: OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
使用時のScriptSig: <signature> <publickey>
検証の概要:
ScriptSigのpublickeyをハッシュして、ScriptPubKeyのPubKeyHashと一致を検証
公開鍵で署名を検証(OP_CHECKSIG)
https://scrapbox.io/files/68896b518b355a32f6b1cc95.png
code:p2pkh
key = Bitcoin::Key.generate
key.to_p2pkh
# 公開鍵取得
pubkey = key.pubkey
# 公開鍵をhash160でハッシュ
# Digest::RMD160.hexdigest(Digest::SHA256.digest(hex.htb))
# hash160はsha256でハッシュ化したものをRIPEMD160でハッシュしたもの
hash160 = Bitcoin.hash160(pubkey)
# hash160をもとに、script_pubkey作成
# OP_DUP OP_HASH160 <PubKeyHash(上記のhash160の値)> OP_EQUALVERIFY OP_CHECKSIG
#
# def self.to_p2pkh(pubkey_hash)
# new << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
# end
script_pubkey = Bitcoin::Script.to_p2pkh(hash160)
# アドレス化
# Bitcoin.encode_base58_address(hash160, Bitcoin.chain_params.address_version)
addr = script_pubkey.to_addr
# デコードするとチェックサムとpubkey_hashの値が取れる
Bitcoin.decode_base58_address(addr)
p2pkhトランザクション
https://explorer.cloverpool.com/ja/btc/transaction/ab291b731f5ccc38d2ce8f51c8fbad5192d92546eee058659fc60cf5e0c187ff
P2SH
複雑なスクリプトを短縮したアドレスフォーマット
ベース58チェックエンコード (3で始まる)
生成の流れ:
redeemScript → SHA-256 → RIPEMD-160 → version byte(0x05) → Checksum → Base58Check
例:
redeemScript(マルチシグ等): 522102abc…2102def…52ae
HASH160(redeemScript): 74c18b6a2b9e0a7a1ad3b1d1c3f4e5d6c7b8a9b0
Version追加: 0574c18b6a2b9e0a7a1ad3b1d1c3f4e5d6c7b8a9b0
Base58Check後: 3CLoMMyuoDQTPRD3XYZvrFQQSLyZBHLYFs
検証スクリプトの流れ:
ScriptPubKey: OP_HASH160 <ScriptHash> OP_EQUAL
ScriptSig (redeemScriptを含む): <signature>... <redeemScript>
流れ:redeemScriptのハッシュが一致 → redeemScriptを実行し検証
※ redeem_script をハッシュしたものを埋め込んで置くため、redeem_scriptを紛失すると使えなくなる
参考:https://docs.google.com/presentation/d/1NyBmv1qYrYWQEYgDG_d9r2DfTbp9jgMUaVzFbE3nXZs/edit?slide=id.g9135c50ad9_0_0#slide=id.g9135c50ad9_0_0
code:p2sh
redeem_script = Bitcoin::Script.from_string("OP_4 OP_ADD OP_6 OP_EQUAL")
redeem_script.to_p2sh.to_h
Segwitの利点
Segwit(Segregated Witness)は、Bitcoinの取引をより効率的に行うために導入されたアップデートです。以下の利点があります。
取引サイズの削減
Witnessデータを別領域に分離することで、トランザクションのサイズを効率的に削減
手数料節約に貢献
トランザクションマリアビリティの解消
トランザクションの署名部分がWitnessに分離されるため、署名データを変更して取引IDを変更する攻撃を防止
トランザクション全体のハッシュ値から、トランザクション全体からwitness領域(署名部分)を取り除いた部分でハッシュ
ライトニングネットワークなどのセカンドレイヤー技術の安全性を向上
スケーラビリティの改善
ブロックにより多くのトランザクションを格納可能に
Bitcoinネットワーク全体の取引処理能力が向上
ブロックサイズの計算方法が変更
1MB制限ではなくWeightで計算
Segwit以前のアドレスのトランザクションだけでブロックを生成すると1MBになる
Segwit以前:https://explorer.cloverpool.com/ja/btc/block/400000
Segwit後:https://explorer.cloverpool.com/ja/btc/block/907752
Segwitアドレス(P2WPKH, P2WSH)
Segwit(BIP141)による新しい形式(ベース58ではなくBech32)
Bech32は人間が読みやすく間違えにくいアドレスフォーマット(bc1で始まる)
スクリプトはWitnessフィールドに含まれる(サイズ削減・マリアビリティ防止)
---------------------------------------------------------------------------------
P2WPKH (公開鍵のSegwit)
公開鍵をWitnessとして扱う
Witnessプログラムが version 0 であり、かつ20バイト(PubkeyHashのサイズ)であるとき、ノードは内部的に以下のP2PKHスクリプトへと展開
アドレス生成:
公開鍵 → HASH160 → Witness Version(0x00) + WitnessProgram → Bech32 encode
例:
公開鍵→ハッシュ160: <hash160(pubkey)>
Bech32: bc1qw4km88h5yp…(以下略)
スクリプト検証(Witness):
ScriptPubKey: 0 <PubKeyHash>
Witnessフィールド: <signature> <publickey>
code:p2wpkh
key = Bitcoin::Key.generate
key.to_p2pkh
# 公開鍵取得
pubkey = key.pubkey
# 公開鍵をhash160でハッシュ
# Digest::RMD160.hexdigest(Digest::SHA256.digest(hex.htb))
# hash160はsha256でハッシュ化したものをRIPEMD160でハッシュしたもの
hash160 = Bitcoin.hash160(pubkey)
# hash160をもとに、script_pubkey作成
# OP_DUP OP_HASH160 <PubKeyHash(上記のhash160の値)> OP_EQUALVERIFY OP_CHECKSIG
#
# def self.to_p2wpkh(pubkey_hash)
# new << WITNESS_VERSION_V0 << pubkey_hash
# end
script_pubkey = Bitcoin::Script.to_p2wpkh(hash160)
# アドレス化
# def bech32_addr
# segwit_addr = Bech32::SegwitAddr.new
# segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
# segwit_addr.script_pubkey = to_hex
# segwit_addr.addr
# end
script_pubkey.to_addr
トランザクション例:
https://explorer.cloverpool.com/btc/transaction/b63c95babacd6c1c90f5970b2ac908e01f195ac6db81db551eb538c3300fc0ce
code:p2wpkh
pubkey = "0244587bb17c3d845ae477a2fb2511ef7233e7b3e8f3ec6f83bd154be74c39bf66"
hash160 = Bitcoin.hash160(pubkey)
script_pubkey = Bitcoin::Script.to_p2wpkh(hash160)
script_pubkey.to_addr
P2WSH (スクリプトのSegwit)
redeemScriptをWitnessとして格納
スクリプトが複雑な場合の最適化
生成の流れ:
redeemScript → SHA-256 → Witness Version(0x00) + WitnessProgram(redeem_scriptのハッシュ値) → Bech32
スクリプト検証:
ScriptPubKey: 0 <SHA256(redeemScript)>
Witnessフィールド: <signature>... <redeemScript>
code:p2wsh
redeem_script = Bitcoin::Script.from_string("OP_4 OP_ADD OP_6 OP_EQUAL")
s = Bitcoin::Script.to_p2wsh(redeem_script)
segwit_addr = Bech32::SegwitAddr.new
segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
segwit_addr.script_pubkey = s.to_hex
segwit_addr.addr
2 of 2 のマルチシグ
https://blockstream.info/tx/b96fab25cec0d2606b443ec0f6d16df723f42ac3922fd387f745063723381ac8
code:redeem_script
# 52 OP_2
# 21 02 144dbe7761433f3e494b443c061f4f50df3f3e45e83f181397f95ff01662109f
# 21 03 774647cfa5a4673c582ead44ec7e56c28734016a5f4f52e334693eb4f59f8594
# 52 OP_2
# ae OP_CHECKMULTISIG
redeem_script_hex = "522102144dbe7761433f3e494b443c061f4f50df3f3e45e83f181397f95ff01662109f2103774647cfa5a4673c582ead44ec7e56c28734016a5f4f52e334693eb4f59f859452ae"
script = Bitcoin::Script.parse_from_payload(redeem_script_hex.pack('H*'))
puts script.to_s
🎯 資料
BIP13 (P2SH): https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki
BIP141 (Segwit): https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
Bech32 (BIP173): https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki